/********************************************
 * 功能说明: 
 * 模块名称: 
 * 系统名称: 
 * 软件版权: 西安华信智慧数字科技有限公司
 * 系统版本: 1.0.0
 * 开发人员: zhangfb
 * 开发时间: 2020/3/1 20:54
 * 审核人员: 
 * 相关文档: 
 * 修改记录: 修改日期 修改人员 修改说明
 *********************************************/
package com.hyacinth.cloudnote.modules.common.filter;

import com.google.common.cache.Cache;
import com.hyacinth.cloudnote.common.security.CurrentUserContextHolder;
import com.hyacinth.cloudnote.common.security.SecurityLog;
import com.hyacinth.cloudnote.common.security.UserPrincipal;
import com.hyacinth.cloudnote.common.security.UserPrincipalParser;
import com.hyacinth.cloudnote.common.utils.CryptoForToken;
import com.hyacinth.cloudnote.common.utils.HttpUtils;
import com.hyacinth.cloudnote.modules.common.config.AppProperties;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 认证过滤器
 *
 * @author zhangfb
 * @version 1.0.0.1
 * @since 2020/3/1 20:54
 */
@Slf4j
@Component
public class ApplicationFilter extends OncePerRequestFilter {

    private static final String SECURITY_IGNORE_URLS_SPILT_CHAR = ",";

    @Autowired
    private AppProperties appProperties;
    @Autowired
    private Cache<String, UserPrincipal> jwtLocalCache;
    @Autowired
    private CryptoForToken cryptoForToken;

    @Override
    protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
        final String excludeAuthUrl = appProperties.getExcludeAuthUrl();
        if (StringUtils.isBlank(excludeAuthUrl)) {
            return false;
        }
        return HttpUtils.antMatchers(request, StringUtils.split(excludeAuthUrl, SECURITY_IGNORE_URLS_SPILT_CHAR));
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        final String requestUri = request.getRequestURI();
        final String clientIp = HttpUtils.getClientIP(request);
        final String method = request.getMethod();
        final String token = request.getHeader(HttpHeaders.AUTHORIZATION);
        SecurityLog.authentication.info("ApplicationFilter [{}] : {} {} {}", clientIp, method, requestUri, token);

        if (StringUtils.isBlank(token)) {
            // 请求头token为空
            SecurityLog.authentication.warn("[{}] Request header token is empty!!!: {} {}", clientIp, requestUri, method);
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "UNAUTHORIZED: Request header token is empty!!!");
            return;
        }

        try {
            // 缓存更新
            UserPrincipal userPrincipal = jwtLocalCache.getIfPresent(token);
            if (userPrincipal == null) {
                CurrentUserContextHolder.clear();
                String decryptToken = cryptoForToken.decrypt(token);
                userPrincipal = UserPrincipalParser.parse(decryptToken);
                // 设置缓存
                jwtLocalCache.put(token, userPrincipal);
            }
            CurrentUserContextHolder.setCurrentUser(userPrincipal);
        } catch (final Exception ex) {
            SecurityLog.authentication.error("[{}] token authentication failed: {} {}, {}, {}, {}",
                    clientIp, method, requestUri, token, ex.getMessage(), ex);
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "UNAUTHORIZED: " + ex.getMessage());
            return;
        }

        filterChain.doFilter(request, response);
    }
}
